package com.stark.wechat.service.user;

import com.dtflys.forest.Forest;
import com.fasterxml.jackson.databind.JsonNode;
import com.stark.wechat.core.Config;
import com.stark.wechat.service.user.model.GetTagFansResponsePayload;
import com.stark.wechat.service.user.model.Tag;
import com.stark.wechat.util.JacksonUtils;
import com.stark.wechat.util.ResponseUtils;
import lombok.Builder;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

import static java.util.Objects.requireNonNull;

/**
 * 用户标签管理接口
 *
 * @author <a href="mailto:mengbin@hotmail.com">Ben</a>
 * @version 1.0.0
 * @since 2024/5/27
 */
@Builder
public class TagService {

    private final Config config;

    /**
     * 获取标签下粉丝列表
     * <p>本接口用于获取标签下粉丝列表。
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param tagId       标签ID
     * @param nextOpenid  第一个拉取的OPENID，不填默认从头开始拉取
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/usermanage/tag/api_gettagfans.html">用户管理 / 标签管理 / 获取标签下粉丝列表</a>
     */
    public GetTagFansResponsePayload getTagFans(String accessToken, Long tagId, String nextOpenid) {
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("tagid", tagId);
        body.put("next_openid", StringUtils.defaultString(nextOpenid));

        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/user/tag/get")
                .contentTypeJson()
                .addQuery("access_token", accessToken)
                .addBody(JacksonUtils.serialize(body))
                .connectTimeout(config.getConnectTimeout())
                .readTimeout(config.getReadTimeout())
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("获取标签下粉丝列表", root, CODE_MAP.get("getTagFans"));
        return JacksonUtils.deserialize(responseStr, GetTagFansResponsePayload.class);
    }

    /**
     * 获取标签
     * <p>本接口用于获取公众号已创建的标签列表。
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @return 公众号已创建的标签
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/usermanage/tag/api_gettags.html">用户管理 / 标签管理 / 获取标签</a>
     */
    public List<Tag> getTags(String accessToken) {
        String responseStr = Forest
                .get(config.getWechatGateway() + "/cgi-bin/tags/get")
                .contentTypeJson()
                .addQuery("access_token", accessToken)
                .connectTimeout(config.getConnectTimeout())
                .readTimeout(config.getReadTimeout())
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("获取标签", root, CODE_MAP.get("getTags"));
        return JacksonUtils.deserializeList(root.get("tags").toPrettyString(), Tag.class);
    }

    /**
     * 创建标签
     * <p>本接口用于创建公众号标签。
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param name        标签名（30个字符以内）
     * @return 标签id，由微信分配
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/usermanage/tag/api_createtag.html">用户管理 / 标签管理 / 创建标签</a>
     */
    public Long createTag(String accessToken, String name) {
        requireNonNull(accessToken);
        requireNonNull(name);

        Map<String, Object> tag = new LinkedHashMap<>();
        tag.put("name", name);
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("tag", tag);

        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/tags/create")
                .contentTypeJson()
                .addQuery("access_token", accessToken)
                .addBody(JacksonUtils.serialize(body))
                .connectTimeout(config.getConnectTimeout())
                .readTimeout(config.getReadTimeout())
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("创建标签", root, CODE_MAP.get("createTag"));
        return root.get("tag").get("id").asLong();
    }

    /**
     * 编辑标签
     * <p>本接口用于修改已存在的标签信息。
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param id          标签id，由微信分配
     * @param name        修改的标签名，UTF8编码
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/usermanage/tag/api_updatetag.html">用户管理 / 标签管理 / 编辑标签</a>
     */
    public void updateTag(String accessToken, Long id, String name) {
        requireNonNull(accessToken);
        requireNonNull(name);

        Map<String, Object> tag = new LinkedHashMap<>();
        tag.put("id", id);
        tag.put("name", name);
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("tag", tag);

        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/tags/update")
                .contentTypeJson()
                .addQuery("access_token", accessToken)
                .addBody(JacksonUtils.serialize(body))
                .connectTimeout(config.getConnectTimeout())
                .readTimeout(config.getReadTimeout())
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("编辑标签", root, CODE_MAP.get("updateTag"));
    }

    /**
     * 删除标签
     * <p>本接口用于删除已存在的标签信息。
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param id          标签id
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/usermanage/tag/api_deletetag.html">用户管理 / 标签管理 / 删除标签</a>
     */
    public void deleteTag(String accessToken, Long id) {
        requireNonNull(accessToken);
        requireNonNull(id);

        Map<String, Object> tag = new LinkedHashMap<>();
        tag.put("id", id);
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("tag", tag);

        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/tags/delete")
                .contentTypeJson()
                .addQuery("access_token", accessToken)
                .addBody(JacksonUtils.serialize(body))
                .connectTimeout(config.getConnectTimeout())
                .readTimeout(config.getReadTimeout())
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("删除标签", root, CODE_MAP.get("deleteTag"));
    }

    /**
     * 批量为用户取消标签
     * <p>本接口用于批量为多个用户取消标签
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param openidList  粉丝openid列表
     * @param tagId       标签id
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/usermanage/tag/api_batchuntagging.html">用户管理 / 标签管理 / 批量为用户取消标签</a>
     */
    public void batchUntagging(String accessToken, List<String> openidList, Long tagId) {
        requireNonNull(accessToken);
        requireNonNull(openidList);
        requireNonNull(tagId);

        Map<String, Object> body = new LinkedHashMap<>();
        body.put("openid_list", openidList);
        body.put("tagid", tagId);

        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/tags/members/batchuntagging")
                .contentTypeJson()
                .addQuery("access_token", accessToken)
                .addBody(JacksonUtils.serialize(body))
                .connectTimeout(config.getConnectTimeout())
                .readTimeout(config.getReadTimeout())
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("批量为用户取消标签", root, CODE_MAP.get("batchUntagging"));
    }

    /**
     * 批量为用户打标签
     * <p>本接口用于批量为多个用户打标签。
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param openidList  粉丝openid列表，最多50个
     * @param tagId       标签id
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/usermanage/tag/api_batchtagging.html">用户管理 / 标签管理 / 批量为用户打标签</a>
     */
    public void batchTagging(String accessToken, List<String> openidList, Long tagId) {
        requireNonNull(accessToken);
        requireNonNull(openidList);
        requireNonNull(tagId);

        Map<String, Object> body = new LinkedHashMap<>();
        body.put("openid_list", openidList);
        body.put("tagid", tagId);

        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/tags/members/batchtagging")
                .contentTypeJson()
                .addQuery("access_token", accessToken)
                .addBody(JacksonUtils.serialize(body))
                .connectTimeout(config.getConnectTimeout())
                .readTimeout(config.getReadTimeout())
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("批量为用户打标签", root, CODE_MAP.get("batchTagging"));
    }

    /**
     * 获取用户的标签列表
     * <p>本接口用于获取粉丝用户的标签列表。
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param openid      用户 openid
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/usermanage/tag/api_batchtagging.html">用户管理 / 标签管理 / 批量为用户打标签</a>
     */
    public List<Long> getTagidList(String accessToken, String openid) {
        requireNonNull(accessToken);
        requireNonNull(accessToken);
        requireNonNull(openid);

        Map<String, Object> body = new LinkedHashMap<>();
        body.put("openid", openid);

        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/tags/getidlist")
                .contentTypeJson()
                .addQuery("access_token", accessToken)
                .addBody(JacksonUtils.serialize(body))
                .connectTimeout(config.getConnectTimeout())
                .readTimeout(config.getReadTimeout())
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("获取用户的标签列表", root, CODE_MAP.get("getTagidList"));
        return JacksonUtils.deserializeList(root.get("tagid_list").toPrettyString(), Long.class);
    }

    private static final Map<String, Map<Long, String>> CODE_MAP;

    static {
        CODE_MAP = new HashMap<>();

        Map<Long, String> codeMap = new HashMap<>();
        codeMap.put(-1L, "系统繁忙，请稍候再试");
        codeMap.put(40001L, "获取 access_token 时 AppSecret 错误，或者 access_token 无效");
        codeMap.put(40003L, "不合法的 OpenID");
        codeMap.put(45159L, "非法的tag_id");
        CODE_MAP.put("getTagFans", Collections.unmodifiableMap(codeMap));

        codeMap = new HashMap<>();
        codeMap.put(-1L, "系统繁忙，请稍候再试");
        codeMap.put(40001L, "获取 access_token 时 AppSecret 错误，或者 access_token 无效");
        CODE_MAP.put("getTags", Collections.unmodifiableMap(codeMap));

        codeMap = new HashMap<>();
        codeMap.put(40001L, "获取 access_token 时 AppSecret 错误，或者 access_token 无效");
        codeMap.put(45056L, "标签数超限");
        codeMap.put(45157L, "标签名非法");
        codeMap.put(45158L, "标签名超长");
        CODE_MAP.put("createTag", Collections.unmodifiableMap(codeMap));

        codeMap = new HashMap<>();
        codeMap.put(40001L, "获取 access_token 时 AppSecret 错误，或者 access_token 无效");
        codeMap.put(45058L, "禁止修改系统标签");
        codeMap.put(45157L, "标签名非法");
        codeMap.put(45158L, "标签名超长");
        CODE_MAP.put("updateTag", Collections.unmodifiableMap(codeMap));

        codeMap = new HashMap<>();
        codeMap.put(40001L, "获取 access_token 时 AppSecret 错误，或者 access_token 无效");
        CODE_MAP.put("deleteTag", Collections.unmodifiableMap(codeMap));

        codeMap = new HashMap<>();
        codeMap.put(-1L, "系统繁忙，请稍候再试");
        codeMap.put(40001L, "获取access_token时AppSecret错误，或者access_token无效");
        codeMap.put(40003L, "不合法的 OpenID");
        codeMap.put(40032L, "不合法的 openid 列表长度");
        codeMap.put(45159L, "非法的标签");
        codeMap.put(45169L, "openid much req");
        codeMap.put(49003L, "传入的openid不属于此AppID");
        CODE_MAP.put("batchUntagging", Collections.unmodifiableMap(codeMap));

        codeMap = new HashMap<>();
        codeMap.put(-1L, "系统繁忙，请稍候再试");
        codeMap.put(40001L, "获取access_token时AppSecret错误，或者access_token无效");
        codeMap.put(40003L, "不合法的 OpenID");
        codeMap.put(40032L, "不合法的 openid 列表长度");
        codeMap.put(45059L, "有粉丝身上的标签数已经超过限制，即超过20个");
        codeMap.put(45159L, "非法的标签");
        codeMap.put(45169L, "openid much req");
        codeMap.put(49003L, "传入的openid不属于此AppID");
        CODE_MAP.put("batchTagging", Collections.unmodifiableMap(codeMap));

        codeMap = new HashMap<>();
        codeMap.put(40003L, "不合法的 OpenID");
        codeMap.put(49003L, "传入的openid不属于此AppID");
        CODE_MAP.put("getTagidList", Collections.unmodifiableMap(codeMap));
    }

}
